using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using HCore.Core;
using HCore.IOC.Beans.Factory.Attribute.Attr;
using HCore.Unity.Behaviour;
using HCore.Unity.Core;
using HCore.Unity.Manager;
using UnityEngine;
using UnityEngine.EventSystems;

namespace HCore.Tests.Runtime.ShootingEditor2D.Scripts.ViewController.LevelEditor
{
    /// <summary>
    ///     操作模式枚举，定义关卡编辑器的操作类型
    /// </summary>
    public enum OperateMode
    {
        /// <summary>
        ///     绘制
        /// </summary>
        Draw,

        /// <summary>
        ///     擦除
        /// </summary>
        Erase
    }

    /// <summary>
    ///     笔刷类型
    /// </summary>
    public enum BrushType
    {
        Ground,
        Player
    }


    /// <summary>
    ///     关卡元素信息类，用于保存关卡中每个元素的基本信息
    /// </summary>
    public class LevelItemInfo
    {
        /// <summary>
        ///     元素名称（通常是标签名）
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        ///     元素X坐标
        /// </summary>
        public float X { get; set; }

        /// <summary>
        ///     元素Y坐标
        /// </summary>
        public float Y { get; set; }
    }

    /// <summary>
    ///     关卡编辑器主控制器，提供绘制、擦除和保存关卡功能
    /// </summary>
    public class LevelEditor : HCoreMonoBehaviour
    {
        private static readonly Color _COLOR_RED = new(1, 0, 0, 0.5f);
        private static readonly Color _COLOR_BLUE = new(0, 0, 1, 0.5f);
        private static readonly Color _COLOR_WHITE = new(1, 1, 1, 0.5f);
        private static readonly Color _COLOR_ORANGE = new(1, 0.5f, 0, 0.5f);

        /// <summary>
        ///     空白区域高亮显示的精灵渲染器
        /// </summary>
        [SerializeField] public SpriteRenderer EmptyHighlight;

        /// <summary>
        ///     当前鼠标悬停的对象
        /// </summary>
        private GameObject mCurrentObjectMouseOn;

        /// <summary>
        ///     输入管理器，用于处理鼠标和键盘输入
        /// </summary>
        [Autowired] private InputManager mInputManager;

        /// <summary>
        ///     预制体管理器，用于加载游戏对象预制体
        /// </summary>
        [Autowired] private PrefabManager mPrefabManager;

        /// <summary>
        ///     当前操作模式的可绑定属性
        /// </summary>
        private BindableProperty<OperateMode> CurrentOperateMode { get; } = new();

        /// <summary>
        ///     当前笔刷类型的可绑定属性
        /// </summary>
        private BindableProperty<BrushType> CurrentBrushType { get; } = new();

        /// <summary>
        ///     初始化关卡编辑器，注册输入监听器和UI事件
        /// </summary>
        protected override void Awake()
        {
            base.Awake();
            mInputManager.AddMouseListener(TriggerPoint.Update, InputType.MouseDown, MouseKey.Left, MouseDown);
            mInputManager.AddMouseListener(TriggerPoint.Update, InputType.Mouse, MouseKey.Left, MouseDown);
            mInputManager.AddKeyboardListener(TriggerPoint.Update, InputType.KeyDown, KeyCode.M, ChangeOperateMode);
            CurrentOperateMode.Register(ActionOperateModeChanged).Trigger();
            CurrentBrushType.Register(ActionBrushTypeChanged).Trigger();

            Get("Canvas/Save:Button").AddButtonClickListener(SaveLevelData);
            Get("Canvas/Draw:Button").AddButtonClickListener(() => CurrentOperateMode.Value = OperateMode.Draw);
            Get("Canvas/Erase:Button").AddButtonClickListener(() => CurrentOperateMode.Value = OperateMode.Erase);
            Get("Canvas/BrushGround:Button").AddButtonClickListener(() => CurrentBrushType.Value = BrushType.Ground);
            Get("Canvas/BrushPlayer:Button").AddButtonClickListener(() => CurrentBrushType.Value = BrushType.Player);
        }

        /// <summary>
        ///     每帧更新逻辑，处理鼠标位置检测和高亮显示
        /// </summary>
        private void Update()
        {
            // IsPointerOverGameObject 返回鼠标是否是否覆盖在UGUI上面
            EmptyHighlight.gameObject.SetActive(!EventSystem.current.IsPointerOverGameObject());
            if (EventSystem.current.IsPointerOverGameObject()) return;

            var mousePosition = Input.mousePosition;
            var mouseWorldPosition = GameUtils.MainCamera.ScreenToWorldPoint(mousePosition);

            // 处理一下，取整，就相当于一个一个的格子了。 看上去就是移动瓦片地图格子
            mouseWorldPosition.z = 0;
            mouseWorldPosition.y = Mathf.Floor(mouseWorldPosition.y + 0.5f);
            mouseWorldPosition.x = Mathf.Floor(mouseWorldPosition.x + 0.5f);

            // 不用每帧都进行检测
            if (Math.Abs(EmptyHighlight.transform.position.x - mouseWorldPosition.x) < 0.1f
                && Math.Abs(EmptyHighlight.transform.position.y - mouseWorldPosition.y) < 0.1f) return;

            var highlightPos = mouseWorldPosition;
            highlightPos.z = -9;
            EmptyHighlight.transform.position = highlightPos;

            // 这里的20 比摄像机照射下来的距离长即可。也可不填写该距离，无限远
            var hit = Physics2D.Raycast(mouseWorldPosition, Vector2.zero, 20);
            // 检测到有碰撞
            if (hit.collider == null)
            {
                mCurrentObjectMouseOn = null;
                EmptyHighlight.color = CurrentOperateMode.Value == OperateMode.Draw ? _COLOR_WHITE : _COLOR_BLUE;
            }
            // 发生了碰撞
            else
            {
                mCurrentObjectMouseOn = hit.collider.gameObject;
                EmptyHighlight.color = CurrentOperateMode.Value == OperateMode.Draw ? _COLOR_RED : _COLOR_ORANGE;
            }
        }

        /// <summary>
        ///     销毁时清理资源，注销所有监听器
        /// </summary>
        protected override void OnDestroy()
        {
            CurrentBrushType.UnRegister(ActionBrushTypeChanged);
            CurrentOperateMode.UnRegister(ActionOperateModeChanged);
            mInputManager.RemoveKeyboardListener(TriggerPoint.Update, InputType.KeyDown, KeyCode.M, ChangeOperateMode);
            mInputManager.RemoveMouseListener(TriggerPoint.Update, InputType.Mouse, MouseKey.Left, MouseDown);
            mInputManager.RemoveMouseListener(TriggerPoint.Update, InputType.MouseDown, MouseKey.Left, MouseDown);
            base.OnDestroy();
        }

        /// <summary>
        ///     保存当前关卡数据到XML文件
        /// </summary>
        private void SaveLevelData()
        {
            // 找到需要序列化的数据
            var infos = new List<LevelItemInfo>(transform.childCount - 1);
            foreach (Transform child in transform)
                if (child.CompareTag("Player") || child.CompareTag("Ground"))
                {
                    var position = child.position;
                    infos.Add(new LevelItemInfo
                    {
                        Name = child.tag,
                        X = position.x,
                        Y = position.y
                    });

                    Debug.Log($"Name:{child.tag}, x:{position.x}, y:{position.y}");
                }


            // 保存数据
            var document = new XmlDocument();
            var declaration = document.CreateXmlDeclaration("1.0", "UTF-8", "");
            document.AppendChild(declaration);

            var level = document.CreateElement("Level");
            document.AppendChild(level);

            foreach (var levelItemInfo in infos)
            {
                var levelItem = document.CreateElement("LevelItem");
                levelItem.SetAttribute("name", levelItemInfo.Name);
                levelItem.SetAttribute("x", $"{levelItemInfo.X}");
                levelItem.SetAttribute("y", $"{levelItemInfo.Y}");
                level.AppendChild(levelItem);
            }

            var levelFilesFolder = $"{Application.persistentDataPath}/LevelFiles";
            Debug.Log($"levelFilesFolder: {levelFilesFolder}");
            if (!Directory.Exists(levelFilesFolder)) Directory.CreateDirectory(levelFilesFolder);

            var levelFilePath = $"{levelFilesFolder}/{DateTime.Now.ToString("yyyyMMddhhmmss")}.xml";
            document.Save(levelFilePath);
            Debug.Log($"已经保存到: {levelFilePath}");
        }


        /// <summary>
        ///     切换操作模式（绘制/擦除）
        /// </summary>
        /// <param name="obj">触发的键盘按键码</param>
        private void ChangeOperateMode(KeyCode obj)
        {
            CurrentOperateMode.Value = CurrentOperateMode.Value == OperateMode.Draw
                ? OperateMode.Erase
                : OperateMode.Draw;
        }

        /// <summary>
        ///     操作模式改变时的回调函数，更新UI显示
        /// </summary>
        /// <param name="newValue">新的操作模式值</param>
        /// <param name="oldValue">旧的操作模式值</param>
        private void ActionOperateModeChanged(OperateMode newValue, OperateMode oldValue)
        {
            var drawMode = newValue == OperateMode.Draw;
            var text = drawMode ? "绘制" : "擦除";
            Get("Canvas/OperateMode:Text").SetText($"工作模式：{text}");
            Get("Canvas/BrushGround:Button").SetActive(drawMode);
            Get("Canvas/BrushPlayer:Button").SetActive(drawMode);
        }

        /// <summary>
        ///     笔刷类型改变时的回调函数，更新UI显示
        /// </summary>
        /// <param name="newValue">新的笔刷类型值</param>
        /// <param name="oldValue">旧的笔刷类型值</param>
        private void ActionBrushTypeChanged(BrushType newValue, BrushType oldValue)
        {
            var text = newValue == BrushType.Ground ? "地板" : "玩家";
            Get("Canvas/Brush:Text").SetText($"笔刷：{text}");
        }

        /// <summary>
        ///     鼠标按下事件处理函数，根据当前操作模式执行绘制或擦除操作
        /// </summary>
        /// <param name="mouseKey">鼠标按键类型</param>
        private void MouseDown(MouseKey mouseKey)
        {
            if (EventSystem.current.IsPointerOverGameObject()) return;
            // 绘制
            if (
                mCurrentObjectMouseOn == null &&
                CurrentOperateMode.Value == OperateMode.Draw)
            {
                GameObject anyGo;
                var emptyHighlightPosition = EmptyHighlight.transform.position;

                switch (CurrentBrushType.Value)
                {
                    case BrushType.Ground:
                        anyGo = mPrefabManager.LoadPrefabGameObject("Prefab/Ground", transform);
                        anyGo.name = $"Ground({(int)emptyHighlightPosition.x}, {(int)emptyHighlightPosition.y})";
                        anyGo.tag = DefaultTags.GROUND;
                        break;
                    case BrushType.Player:
                        anyGo = mPrefabManager.LoadPrefabGameObject("Prefab/Ground", transform);
                        anyGo.name = "Player";
                        anyGo.tag = DefaultTags.PLAYER;
                        anyGo.GetComponent<SpriteRenderer>().color = Color.cyan;
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }

                anyGo.transform.position = new Vector3(emptyHighlightPosition.x, emptyHighlightPosition.y, 0);

                // 设置Ground后，及时调整参数未不能重复设置，调整高亮坐标，让射线重新碰撞计算一下
                EmptyHighlight.color = _COLOR_RED;
                mCurrentObjectMouseOn = anyGo;
            }
            // 擦除
            else if (mCurrentObjectMouseOn != null &&
                     CurrentOperateMode.Value == OperateMode.Erase)
            {
                Destroy(mCurrentObjectMouseOn);
                mCurrentObjectMouseOn = null;
            }
        }
    }
}